{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Functions and Variable Scope\n",
    "\n",
    "**Time**\n",
    "\n",
    "- teaching: 10 min\n",
    "- exercises: 10 min\n",
    "\n",
    "**Questions**:\n",
    "\n",
    "- \"How do function calls actually work?\"\n",
    "\n",
    "**Learning Objectives**:\n",
    "\n",
    "- \"Identify local and global variables.\"\n",
    "- \"Identify parameters as local variables.\"\n",
    "- \"Identify default arguments\"\n",
    "* * * * *\n",
    "\n",
    "\n",
    "## Functions are an Alternative Reality\n",
    "\n",
    "When you call a function, a temporary workspace is set up that will be destroyed when the function returns by:\n",
    "\n",
    "1. getting to the end, or \n",
    "1. explicity by a `return` statement\n",
    "\n",
    "So think of functions as an alternative reality, whereby variables are created and destroyed in a function call."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def increment(num):\n",
    "    incremented_num = num + 1\n",
    "    return(incremented_num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "increment(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this temporary environment, the variables in the [parameter](https://github.com/dlab-berkeley/python-intensive/blob/master/Glossary.md#parameter) list (in parentheses in the definition) are set to the values passed in. For example, in `increment(3)`, `num` gets set to `3`. Afterwards, you can't access these variables!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the cell above we get an error because `num` is no longer defined. This is because the `increment(num)` function has already returned (i.e., finished) and so it's temporary scope has been destroyed!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(incremented_num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Same with the local variable `incremented_num` that was created within `increment(num)`!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The scope of a variable is the part of a program that can 'see' that variable.\n",
    "\n",
    "Things can get confusing when you use the same names for variables both inside and outside a function. Check out this example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pressure = 103.9\n",
    "\n",
    "def adjust(t):\n",
    "    temperature = t * 1.43 / pressure\n",
    "    return(temperature)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"pressure is\", pressure)\n",
    "print(\"temperature is\", temperature)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*   `pressure` is a *global variable*.\n",
    "    *   Defined outside any particular function.\n",
    "    *   Visible everywhere.\n",
    "*   `t` and `temperature` are *local variables* in `adjust`.\n",
    "    *   Defined in the function.\n",
    "    *   Not visible in the main program.\n",
    "    *   Remember: a function parameter is a variable\n",
    "        that is automatically assigned a value when the function is called."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Gotcha!\n",
    "\n",
    "But once we start using **mutable** data types like lists, things become tricky:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = [1, 2, 3, 5]\n",
    "\n",
    "def add_3(val):\n",
    "    val[0] = val[0] + 3\n",
    "    return val\n",
    "\n",
    "add_3(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, our function is modifying the contents of the list, and both variables still point to the same list.\n",
    "\n",
    "So the list `x` refers to *is* modified"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, the issue here is our function is no longer changing `val` so that it points at a new \"thing.\" Instead, we're taking the list that `val` points to (the same list `x` points to) and modifying it.\n",
    "\n",
    "Tricky, but important!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Arguments can have defaults\n",
    "\n",
    "Functions do not need to take input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_hello():\n",
    "    print(\"hello\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print_hello()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But if a function takes input, arguments can be passed to functions in different ways.\n",
    "\n",
    "1) **Positional arguments** are mandatory and have no default values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def send(message, recipient):\n",
    "    \"\"\" Prints a \"message\" to the \"recipient\"\n",
    "    The function returns nothing, which defaults to None\"\"\"\n",
    "    print(message, recipient)\n",
    "    \n",
    "send('Hello','World')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the case above, it is possible to use [argument](https://github.com/dlab-berkeley/python-intensive/blob/master/Glossary.md#argument) names when calling the functions and, doing so, it is possible to switch the order of arguments, calling for instance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "send(recipient='World', message='Hello')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But this reduces readability and is unnecessarily verbose, compared to the more straightforward calls to `send('Hello', 'World')`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2) **Keyword arguments** are not mandatory and have default values. They are often used for optional parameters sent to the function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def send(message, recipient, cc=None, bcc=None):\n",
    "    \"\"\" Prints a kind greeting to our input\n",
    "    returns nothing\"\"\"\n",
    "    print(message, recipient)\n",
    "    if cc is not None:\n",
    "        print(\"CC: \", cc)\n",
    "    if bcc is not None:\n",
    "        print(\"BCC: \", bcc)\n",
    "    \n",
    "send('Hello','World')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here cc and bcc are optional, and evaluate to `None` when they are not passed another value."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "send('Hello','World', \"Rochelle\", \"Laura\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To explicitly use only one optional parameter, specify the name of the parameter followed by an equals sign, and the argument value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "send('Hello','World', bcc=\"Laura\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But if we don't explicitly state the option parameter to be used, Python will use the order the parameters are specified in the function declaration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "send('Hello','World', \"Laura\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Challenge 1:\n",
    "\n",
    "Write a function (with your neighbor!), which takes multiple arguments and returns a value. Be creative, and think about what may be useful to you and your research (*What is something you may need to do to your data many times, but the parameters change each time depending on the input?*). Use your resources to solve problems we haven't encountered yet!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Functions Checklist\n",
    "\n",
    "**Defining functions checklist**\n",
    "\n",
    "1. Did you start your function definition with def?\n",
    "2. Does your function name have only characters and `_` (underscore) characters?\n",
    "3. Did you put an open parenthesis `(` right after the function name?\n",
    "4. Did you put your arguments after the parenthesis `(` separated by commas?\n",
    "5. Did you make each argument unique (meaning no duplicated names)?\n",
    "6. Did you put a close parenthesis and a colon `):` after the arguments?\n",
    "7. Did you indent all lines of code you want in the function four spaces? No more, no less.\n",
    "8. Did you \"end\" your function by going back to writing with no indent (dedenting we call it)?\n",
    "\n",
    "**Calling functions checklist**\n",
    "\n",
    "1. Did you call/use/run this function by typing its name?\n",
    "2. Did you put the values you want into the parenthesis separated by commas?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
